#include "StaticModel.h"

//So we can have per vertex structures. 
#include "TileForwardNormalMapFX.h"
#include "TileForwardTextureMapFX.h"
#include "TileForwardSolidColourFX.h"

//Include OBJLoader as we will have an instance
//of this inside the implementation of LoadOBJ()
#include "OBJLoader.h"

//Include assimp headers (c++)
#include <assimp\Importer.hpp>
#include <assimp\types.h>
#include <assimp\postprocess.h>
#include <assimp\scene.h>
#include <assimp\mesh.h>
#include <assimp\material.h>
#include <assimp\color4.inl>
#include <assimp\vector3.inl>
#include <assimp\vector2.inl>



StaticModel::StaticModel()
{
	//Init world matrix
	position = XMFLOAT3(0.0f, 0.0f, 0.0f);
	rotation = XMFLOAT3(0.0f, 0.0f, 0.0f);
	scaling = XMFLOAT3(1.0f, 1.0f, 1.0f);

	CreateWorldMatrix();

	//No bounding box initially. 
	aabbL.Center = XMFLOAT3(0.0f, 0.0f, 0.0f);
	aabbL.Extents = XMFLOAT3(0.0f, 0.0f, 0.0f);

	//Init other data
	totalDrawCount = 0;

	//Will cast shadows by default
	isShadowCaster = true;
	shouldSampleSSAOMap = true;

	//Not inited yet.
	isInit = false;
}

StaticModel::~StaticModel()
{
	//Clear system memory vertex copy vector
	systemVertexCopyArr.clear();

	//Clear materials
	for (unsigned i = 0; i < subModels.size(); i++)
		SafeDelete(subModels[i]);

	subModels.clear();
}

void StaticModel::SetAssimpModelsTessellationData(TessellationData& tessData)
{
	for (unsigned i = 0; i < subModels.size(); i++)
		subModels[i]->TessData = tessData;
}


void StaticModel::SetSubmodelsWithSameMaterial(Material& material)
{
	for (unsigned i = 0; i < subModels.size(); i++)
	{
		subModels[i]->Mat = material;
		if (material.Diffuse.w < 1.0f)
			subModels[i]->IsOpaque = false;
		else
			subModels[i]->IsOpaque = true;
	}
}

void StaticModel::SetSubModelMaterial(unsigned index, Material& material)
{
	if (index < subModels.size())
	{
		subModels[index]->Mat = material;
		if (material.Diffuse.w < 1.0f)
			subModels[index]->IsOpaque = false;
		else
			subModels[index]->IsOpaque = true;
	}
	else
		OutputDebugStringW(L"Error - Trying to update submodels material. Index out of bounds\n");
}

void StaticModel::SetSubmodelsWithSameReflectionData(XMFLOAT3 &reflectivity)
{
	XMFLOAT4 r = XMFLOAT4(reflectivity.x, reflectivity.y, reflectivity.z, 1.0f);
	for (unsigned i = 0; i < subModels.size(); i++)
		subModels[i]->Mat.Reflection = r;
}

void StaticModel::SetWorldPosition(XMFLOAT3 pos)
{
	//store position
	position = pos;

	//Create world matrix.
	CreateWorldMatrix();
}

void StaticModel::SetWorldRotation(XMFLOAT3 rot)
{
	//store rotation
	rotation = rot;
	
	//Create world matrix.
	CreateWorldMatrix();
}

void StaticModel::SetWorldScale(XMFLOAT3 scale)
{
	//store scaling factor
	scaling = scale;
	
	//Create world matrix.
	CreateWorldMatrix();
}

void StaticModel::SetWorldMatrix(XMFLOAT3 pos, XMFLOAT3 rot, XMFLOAT3 scale)
{
	//Store pos, rot and scale
	position = pos;
	rotation = rot;
	scaling = scale;

	//Create world matrrix.
	CreateWorldMatrix();
}

void StaticModel::CreateWorldMatrix()
{
	//Create world matrix. 
	XMMATRIX trans = XMMatrixTranslation(position.x, position.y, position.z);
	XMMATRIX rotationX = XMMatrixRotationX(rotation.x);
	XMMATRIX rotationY = XMMatrixRotationY(rotation.y);
	XMMATRIX rotationZ = XMMatrixRotationZ(rotation.z);
	XMMATRIX rotationM = (rotationX * rotationY) * rotationZ;
	XMMATRIX scaleM = XMMatrixScaling(scaling.x, scaling.y, scaling.z);
	XMMATRIX world = (scaleM * rotationM) * trans;

	//Store in to newMaterial
	XMStoreFloat4x4(&worldMatrix, world);
}

//
// Create standard (GeoGen) geometry
//

void StaticModel::CreateBox(float width, float height, float depth,
	StaticModelBasicMaterial* material,
	ID3D11Device* device)
{
	//Ensure this instance has not been inited yet. 
	if (!isInit)
	{
		GeometryGenerator geoGen;
		GeometryGenerator::MeshData data;

		//Create the box
		geoGen.CreateBox(width, height, depth, data);
		LoadSimpleModelData(device, material, data);

		//Create AABB
		CreateAABB();

		//Set init flag to true.
		isInit = true;
	}
	else
		//Instance already inited.
		OutputDebugStringW(L"StaitcModel Error: Instance already inited. Can not create box\n");
}

void StaticModel::CreateSphere(float radius, unsigned sliceCount, unsigned stackCount,
		StaticModelBasicMaterial* material,
		ID3D11Device* device)
{
	//Ensure this instance has not been inited yet. 
	if (!isInit)
	{
		//Create sphere
		GeometryGenerator geoGen;
		GeometryGenerator::MeshData data;

		geoGen.CreateSphere(radius, sliceCount, stackCount, data);
		LoadSimpleModelData(device, material, data);

		//Create AABB
		CreateAABB();

		//Set init flag to true.
		isInit = true;
	}
	else
		//Instance already inited.
		OutputDebugStringW(L"StaticModel Error: Instance already inited. Can not create sphere\n");
}

void StaticModel::CreateGeoSphere(float radius, unsigned subDivisions, 
		StaticModelBasicMaterial* material,
		ID3D11Device* device)
{
	//Ensure this instance has not been inited yet. 
	if (!isInit)
	{
		//Create sphere
		GeometryGenerator geoGen;
		GeometryGenerator::MeshData data;

		geoGen.CreateGeosphere(radius, subDivisions, data);
		LoadSimpleModelData(device, material, data);

		//Create AABB
		CreateAABB();

		//Set init flag to true.
		isInit = true;
	}
	else
		//Instance already inited.
		OutputDebugStringW(L"StaticModel Error: Instance already inited. Can not create geosphere\n");
}

void StaticModel::CreateCollum(float bottomRadius, float topRadius, float height,
		unsigned sliceCount, unsigned stackCount, 
		StaticModelBasicMaterial* material,
		ID3D11Device* device)
{
	//Ensure this instance has not been inited yet. 
	if (!isInit)
	{
		//Create sphere
		GeometryGenerator geoGen;
		GeometryGenerator::MeshData data;

		geoGen.CreateCylinder(bottomRadius, topRadius, height, sliceCount, stackCount, data);
		LoadSimpleModelData(device, material, data);

		//Create AABB
		CreateAABB();

		//Set init flag to true.
		isInit = true;
	}
	else
		//Instance already inited.
		OutputDebugStringW(L"StaticModel Error: Instance already inited. Can not create collum\n");
}

void StaticModel::CreateGrid(float width, float height, 
		unsigned numVerticiesM, unsigned numVerticiesN, 
		StaticModelBasicMaterial* material,
		ID3D11Device* device)
{
	//Ensure this instance has not been inited yet. 
	if (!isInit)
	{
		//Create sphere
		GeometryGenerator geoGen;
		GeometryGenerator::MeshData data;

		geoGen.CreateGrid(width, height, numVerticiesM, numVerticiesN, data);
		LoadSimpleModelData(device, material, data);

		//Create AABB
		CreateAABB();

		//Set init flag to true.
		isInit = true;
	}
	else
		//Instance already inited.
		OutputDebugStringW(L"StaticModel Error: Instance already inited. Can not create grid\n");
}

void StaticModel::LoadSimpleModelData(ID3D11Device* device, 
		StaticModelBasicMaterial* material,
		GeometryGenerator::MeshData& data)
{
	//Cretae a new material.
	SubModelData* newMaterial = new SubModelData();

	//Loop through and extract vertex data - saving the position data.
	std::vector<TileForwardNormalMapFX::PosNormalTexTangentVertex>vertexTangnet;
	std::vector<TileForwardTextureMapFX::PosNormalTexVertex>vertexTexture;
	std::vector<TileForwardSolidColourFX::PosNormalVertex>vertexNormal;

	for (unsigned i = 0; i < data.Vertices.size(); i++)
	{
		//Store position.
		systemVertexCopyArr.push_back(data.Vertices[i].Position);

		//VB With tangent data - normal mapping or displacement mapping.
		if ( ((material->UseNormalMap) || (material->UseDisplacementMap)) && (material->UseTextureMap) )
		{
			//Copy data for this vertex in to a new struct.
			TileForwardNormalMapFX::PosNormalTexTangentVertex dataToAppend;

			//Copy data for each vertex
			dataToAppend.PosL = data.Vertices[i].Position;
			dataToAppend.NormalL = data.Vertices[i].Normal;
			dataToAppend.TangentL = data.Vertices[i].TangentU;
			dataToAppend.Tex = data.Vertices[i].TexC;

			//Push on to vector.
			vertexTangnet.push_back(dataToAppend);
		}
		//VB Without tangent data - Plain textured object
		else if (material->UseTextureMap)
		{		
			//Copy data for this vertex in to a new struct.
			TileForwardTextureMapFX::PosNormalTexVertex dataToAppend;

			//Copy data for each vertex
			dataToAppend.PosL = data.Vertices[i].Position;
			dataToAppend.NormalL = data.Vertices[i].Normal;
			dataToAppend.Tex = data.Vertices[i].TexC;

			//Push on to vector.
			vertexTexture.push_back(dataToAppend);
		}
		//VB without texcoord data - plain coloured model.
		else
		{
			//Copy data for this vertex in to a new strucuture
			TileForwardSolidColourFX::PosNormalVertex dataToAppend;

			//Copy data for each vertex
			dataToAppend.PosL = data.Vertices[i].Position;
			dataToAppend.NormalL = data.Vertices[i].Normal;

			//Push on to vector. 
			vertexNormal.push_back(dataToAppend);
		}
	}

	//Create vertex buffer.
	//
	//Fillout vertex buffer description.
	D3D11_BUFFER_DESC bd;
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	if ( ((material->UseNormalMap) || (material->UseDisplacementMap)) && (material->UseTextureMap) )
		bd.ByteWidth = sizeof(TileForwardNormalMapFX::PosNormalTexTangentVertex) 
			* data.Vertices.size();
	else if (material->UseTextureMap)
		bd.ByteWidth = sizeof(TileForwardTextureMapFX::PosNormalTexVertex) 
			* data.Vertices.size();
	else
		bd.ByteWidth = sizeof(TileForwardSolidColourFX::PosNormalVertex) 
			* data.Vertices.size();

	bd.CPUAccessFlags = 0;
	bd.MiscFlags = 0;
	bd.StructureByteStride = 0;
	bd.Usage = D3D11_USAGE_IMMUTABLE;

	//Fillout subresource data description - our init data
	D3D11_SUBRESOURCE_DATA sd;
	if ( ((material->UseNormalMap) || (material->UseDisplacementMap)) && (material->UseTextureMap) )
		sd.pSysMem = &vertexTangnet[0];
	else if ((material->UseTextureMap))
		sd.pSysMem = &vertexTexture[0];
	else
		sd.pSysMem = &vertexNormal[0];

	//Create VB in VRam
	HR(device->CreateBuffer(&bd, &sd, &newMaterial->vb));

	//Create indexs buffer
	//
	//Fillout vertex buffer description.
	bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bd.ByteWidth = sizeof(UINT) * data.Indices.size();
		
	//Fillout Subresource data struct.
	sd.pSysMem = &data.Indices[0];

	//Create IB in Vram
	HR(device->CreateBuffer(&bd, &sd, &newMaterial->ib));

	//Load texture
	if (material->UseTextureMap)
		HR(D3DX11CreateShaderResourceViewFromFile(device, 
			material->TextureMapFilePath,
			0, 0, &newMaterial->TextureMap,
			0));

	//Load normal map
	if ( (material->UseNormalMap) && (material->UseTextureMap) )
		HR(D3DX11CreateShaderResourceViewFromFile(device, 
			material->NormalMapFilePath,
			0, 0, &newMaterial->NormalMap,
			0));

	//Load Displacement map. Check if normal map has been loaded
	//already. If true, we use that. If false (programmer has set 
	//normal mapping flag to false but set the displacement map flag
	//to true, load the file specified by the normal map file location
	//string)
	if ( (material->UseDisplacementMap) && (material->UseTextureMap) )
		if (material->UseNormalMap)
			newMaterial->IsUsingDisplacementMapping = true;
		else
		{
			//Load displacement map - the normal map. 
			HR(D3DX11CreateShaderResourceViewFromFile(device, 
				material->NormalMapFilePath,
				0, 0, &newMaterial->NormalMap,
				0));

			newMaterial->IsUsingDisplacementMapping = true;
		}

		//Load seperate disp map. 
		if ( (material->UseSeperateHeightMap) && (material->UseDisplacementMap) &&
			 (material->UseTextureMap) )
		{
			newMaterial->IsUsingDisplacementMapping = true;
			newMaterial->IsUsingSeperateHeightMap = true;
			//Load seperate height map
			HR(D3DX11CreateShaderResourceViewFromFile(device, 
				material->HeightMapFilePath, 
				0, 0, &newMaterial->HeightMap, 
				0));
		}
	
	//Store draw count - one material for the entire model.
	newMaterial->DrawCount = data.Indices.size();
	newMaterial->StartDrawIndex = 0;

	//Add drawCount to the classes totalDrawCount.
	totalDrawCount += data.Indices.size();

	//Is object opauque?
	newMaterial->IsOpaque = material->IsOpaque;

	//Store lighting material
	newMaterial->Mat = material->Material;

	//Create tex transformation matrix.
	XMMATRIX texTr = XMMatrixScaling(material->TexScale.x, material->TexScale.y, 1.0f);
	//Store
	XMStoreFloat4x4(&newMaterial->TexTrans, texTr);

	//Store tessellation data
	newMaterial->TessData = material->TessData;

	//If tessellated, use 3_POINT_PATCH_LIST
	if (newMaterial->IsUsingDisplacementMapping)
		newMaterial->Topology = D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST;

	//Push material on to vector.
	subModels.push_back(newMaterial);
}

void StaticModel::CreateAABB()
{
	//AABB - min and max xyz values. 
	XMFLOAT3 min = XMFLOAT3(+MathHelper::Infinity, +MathHelper::Infinity, +MathHelper::Infinity);
	XMFLOAT3 max = XMFLOAT3(-MathHelper::Infinity, -MathHelper::Infinity, -MathHelper::Infinity);

	XMVECTOR vMin = XMLoadFloat3(&min);
	XMVECTOR vMax = XMLoadFloat3(&max);

	//Loop through system copy of positions
	for (unsigned i = 0; i < systemVertexCopyArr.size(); i++)
	{
		//Load position in to a vector.
		XMVECTOR point = XMLoadFloat3(&systemVertexCopyArr[i]);

		//Update min/max
		vMin = XMVectorMin(vMin, point);
		vMax = XMVectorMax(vMax, point);
	}

	//outputting maxium and minium vectors so the centre can be checked manualy for 
	//debuging
	//XMFLOAT3 temp;
	
	//convert vector to float 3
	//XMStoreFloat3(&temp,vMin);

	//convert float to string
	//std::stringstream ss2;
	//ss2 << temp.y;
	//std::string str2 = ss2.str();

	//output value
	//OutputDebugStringW(L"Vector Output start\n");
	//OutputDebugStringA(str2.c_str());
	//OutputDebugStringW(L"\n");

	//clear string and stream
	//str2.clear();
	//ss2.clear();

	//convert vector to float 3
	//XMStoreFloat3(&temp,vMax);

	//convert float to string
	//ss2 << temp.y;
	//str2 = ss2.str();

	////output value
	//OutputDebugStringA(str2.c_str());
	//OutputDebugStringW(L"\n");
	//OutputDebugStringW(L"Vector Output end\n");

	//Work out center point and the extents
	XMVECTOR center = 0.5f * (vMin + vMax);
	XMVECTOR extend = 0.5f * (vMax - vMin);

	//Convert to XMFLOAT3 format. 
	XMFLOAT3 fC;
	XMFLOAT3 fE;
	XMStoreFloat3(&fC, center);
	XMStoreFloat3(&fE, extend);

	//update AABB structure.
	aabbL.Center = fC;
	aabbL.Extents = fE;

	//Create OABB. 
	XNA::ComputeBoundingOrientedBoxFromPoints(&oabb, systemVertexCopyArr.size(), &systemVertexCopyArr[0], sizeof(XMFLOAT3));
}

//
//External file loading
//

void StaticModel::CreateModelFromOBJFile(
		ID3D11Device* device,
		LPCWSTR objFilePath,			 
		std::vector<LPCWSTR> &objMaterialFilePath,  
		std::string textureFolderPath,	               
		bool useDisplacementMapping, 
		TessellationData* tessData,	  
		XMFLOAT2 texScale)
{
	if (!isInit)
	{
		//Check for error
		if ( (useDisplacementMapping) && (tessData == 0) )
		{
			OutputDebugStringW(L"Static Model Error : Wanting to use displacement mapping ");
			OutputDebugStringW(L"but no tessellation data supplied\n");
			while(1){}
		}

		//Ask the OBJ loader to do its thing.
		OBJLoader loader;
		loader.LoadOBJ(device,
			this, 
			objFilePath,
			objMaterialFilePath, 
			textureFolderPath, 
			useDisplacementMapping, 
			tessData, 
			texScale);

		//Set isInit flag to true once the model has loaded sucessfully.
		isInit = true;
	}
	else
		OutputDebugStringW(L"Static Model Error - Model already inited - can not load obj\n");
}

void StaticModel::CreateModelThroughAssimp(ID3D11Device* device, std::string& file, 
	std::string& textureDirectory, 
	bool shouldMakeLeftHanded, bool shouldFlipUV, bool shouldFlipWinding, 
	bool shouldFixInwardFacingNormals, 
	bool loadDisplacementMapIfAvailable)
{
	if (!isInit)
	{
		//Print message informing user what file we are loading. 
		OutputDebugStringW(L"Loading through Assimp: ");
		OutputDebugStringA(file.c_str());
		OutputDebugStringW(L"\n");

		//Construct an assimp importer instance - imports
		//models to an aiScene which contains the mesh data for
		//the model. 
		Assimp::Importer asImp;

		//Select which post processes we want to apply to the model - This can slow
		//model loading down, however it should be faster to render - Plus, we can right all sorts
		//of poor models. . 
		unsigned int ppFlags = 0;
		ppFlags = aiProcess_GenSmoothNormals					 //If normals are not loaded, generate them.
			| aiProcess_CalcTangentSpace						 //Generates tangent vectors. 
			| aiProcess_JoinIdenticalVertices					 //When used, we need to create an index buffer.
			| aiProcess_Triangulate								 //Triangulates quads automatically. 
			| aiProcess_GenUVCoords								 //Gens tex coords if not present (I think...)
			| aiProcess_OptimizeMeshes							 //Aims to reduce draw calls - which is good. 
			| aiProcess_SplitLargeMeshes;						 //Only when required (On very large meshes)

		//Optional flags.
		if (shouldMakeLeftHanded)
			ppFlags |= aiProcess_MakeLeftHanded;				 //If model was built in right hand system
		if (shouldFlipUV)
			ppFlags |=  aiProcess_FlipUVs;						 //If UV (0,0) is bottom left
		if (shouldFlipWinding)
			ppFlags |= aiProcess_FlipWindingOrder;				 //Flips from CCW to CW or CW to CCW. 
		if (shouldFixInwardFacingNormals)
			ppFlags |= aiProcess_FixInfacingNormals;			 //Finds inward facing normals and flips - Often not needed.               

		//Load model from file. Stored in aiScene. 
		const aiScene* scene = asImp.ReadFile(file, ppFlags);

		//if scene is NULL, an error occured. 
		if (!scene)
		{
			OutputDebugStringW(L"Static Model Error - Error parsing: ");
			OutputDebugStringA(file.c_str());
			OutputDebugStringW(L". Error String: ");
			OutputDebugStringA(asImp.GetErrorString());
			OutputDebugStringW(L"\n");
			while(1){}
		}

		//If the scene has animations, print warning.
		if (scene->HasAnimations())
		{
			OutputDebugStringW(L"Static Model Warning - File has Animation data. This ");
			OutputDebugStringW(L"will be ignored. If you need it, use AnimatingModel\n");
		}

		//Loop through meshes. We create a single submodel per
		//mesh.
		for (unsigned i = 0; i < scene->mNumMeshes; i++)
			LoadSubModelThroughAssimp(device, scene, i, textureDirectory, loadDisplacementMapIfAvailable);

		//Create AABB
		CreateAABB();

		//Once done loading, set isInit flag to true. 
		isInit = true;
	}
	else
		OutputDebugStringW(L"Static Model Error - Model already inited - can not load Assimp model\n");
}

void StaticModel::LoadSubModelThroughAssimp(ID3D11Device* device, const aiScene* scene, unsigned meshIndex, 
	std::string& textureDir, bool loadDispMap)
{
	//Submodel for this mesh and material. 
	SubModelData* subModel = new SubModelData();

	//Get ith mesh (Vertex/index data). 
	aiMesh* mesh = scene->mMeshes[meshIndex];
	//Get this meshes material index. We can use this to get the
	//aiMaterial struct for this submodel
	aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];

	//Set SubModel Properties that are the same for all Assimp loaded
	//models. 
	XMStoreFloat4x4(&subModel->TexTrans, XMMatrixScaling(1.0f, 1.0f, 1.0f));
	subModel->StartDrawIndex = 0;

	//Load material data.
	LoadAssimpMaterial(device, material, subModel, textureDir, loadDispMap);
	//Load vertex (And index) data.
	LoadAssimpVertexData(device, mesh, subModel);

	//Push submodel on to vector.
	subModels.push_back(subModel);
}

void StaticModel::LoadAssimpMaterial(ID3D11Device* device, aiMaterial* material, SubModelData* subModel, 
	std::string &textureDir, bool loadDispMap)
{
	//Load the material
	//
	//Get diffuse map path if one exists. Else its a flat colour. 
	if (material->GetTextureCount(aiTextureType_DIFFUSE) > 0)
	{
		//Get filename of the texture. 
		aiString path;
		if (material->GetTexture(aiTextureType_DIFFUSE, 0, &path) == AI_SUCCESS)
		{
			//Success - we have the texture file name - Concatinate with 
			//the passed in texture directory to get the full filepath
			std::string fullFilePath = textureDir + path.data;

			OutputDebugStringW(L"Loading Diffuse map...\n");

			//Load texture map.
			LoadTexture(device, fullFilePath, &subModel->TextureMap);
		}
	}

	//Get normal map path if one exists. (EDIT: Assimp is broken. Seems to store
	//normal maps in aiTextureType_HEIGHT and NOT ..._NORMALS for some unknown reason. Made relivent
	//changes on 12.05.14)
	if ( (material->GetTextureCount(aiTextureType_HEIGHT) > 0) || (material->GetTextureCount(aiTextureType_NORMALS) > 0) )
	{
		//Get filename of the normal map (only the one supproted
		//at the moment)
		aiString path;
		if (material->GetTexture(aiTextureType_HEIGHT, 0, &path) == AI_SUCCESS)
		{
			//Found the texture file name - Load it. 
			std::string fullFilePath = textureDir + path.data;

			OutputDebugStringW(L"Loading Normal Map (HEIGHT)...\n");

			//Load normal map
			LoadTexture(device, fullFilePath, &subModel->NormalMap);
		}
		else if(material->GetTexture(aiTextureType_NORMALS, 0, &path) == AI_SUCCESS)
		{
			//Found the texture file name - Load it. 
			std::string fullFilePath = textureDir + path.data;

			OutputDebugStringW(L"Loading Normal Map (NORMALS)...\n");

			//Load normal map
			LoadTexture(device, fullFilePath, &subModel->NormalMap);
		}
	}

	//Get height map (if one exists and option ebaled) - Can only be
	//a displacement map.
	//
	//Note: Removed test for aiTextureType_HEIGHT as this seems to be a normal
	//map and NOT a height map - assimp seems broken. See: www.gamedev.net/topic/617668-assimp-normal-maps/
	if (loadDispMap)
	{
		if (material->GetTextureCount(aiTextureType_DISPLACEMENT) > 0)
		{
			//If one is found, we are using displacement mapping for this
			//model.
			subModel->IsUsingDisplacementMapping = true;
			subModel->IsUsingSeperateHeightMap = true;

			//Get filename of the disp map.
			aiString path;
			if (material->GetTexture(aiTextureType_DISPLACEMENT, 0, &path) == AI_SUCCESS)
			{
				//Found the disp map file name. Load
				std::string fullFilePath = textureDir + path.data;

				//Load disp map.
				LoadTexture(device, fullFilePath, &subModel->HeightMap);
			}
		}
	}

	//Get lighting material data (reflection data). Material::Diffuse.A
	//is the opacity. If < 1.0, then we have a transparant Sub Model.
	aiColor3D d(1.f, 1.f, 1.f); //Diffuse
	float o = 1.0f;				//Diffuse.w
	aiColor3D s(1.f, 1.f, 1.f); //Spec
	float sh = 64.0f;			//Spec shinyness
	aiColor3D a(1.f, 1.f, 1.f); //Ambient
	aiColor3D ref(0.0f, 0.0f, 0.0f); //Reflectivity. 


	//Get material colours. 
	material->Get(AI_MATKEY_COLOR_DIFFUSE, d); 
	material->Get(AI_MATKEY_OPACITY, o);
	material->Get(AI_MATKEY_COLOR_SPECULAR, s);
	material->Get(AI_MATKEY_SHININESS_STRENGTH, sh);
	material->Get(AI_MATKEY_COLOR_AMBIENT, a);
	material->Get(AI_MATKEY_COLOR_REFLECTIVE, ref);

	//Flags to indicate if anything is (0,0,0). If all are false,
	//we use the default material. Else, use the ones loaded. 
	bool diffAv = true;
	bool specAv = true;
	bool ambAv = true;

	if (d.r == 0.0f && d.g == 0.0f && d.b == 0.0)
		diffAv = false;
	if (s.r == 0.0f && s.g == 0.0f && s.b == 0.0)
		specAv = false;
	if (a.r == 0.0f && a.g == 0.0f && a.b == 0.0)
		ambAv = false;

	//If one of the above is available, set material. Else
	//use default.
	if (diffAv || specAv || ambAv)
	{
		subModel->Mat.Diffuse.x = d.r;
		subModel->Mat.Diffuse.y = d.g;
		subModel->Mat.Diffuse.z = d.b;
		subModel->Mat.Diffuse.w = o;

		subModel->Mat.Specular.x = s.r;
		subModel->Mat.Specular.y = s.g;
		subModel->Mat.Specular.z = s.b;
		subModel->Mat.Specular.w = sh;

		subModel->Mat.Ambient.x = a.r;
		subModel->Mat.Ambient.y = a.g;
		subModel->Mat.Ambient.z = a.b;
		subModel->Mat.Ambient.w = 1.0f; //Not utilised. 
	}
	else
	{
		OutputDebugStringW(L"Assimp Warning: No reflectivity data detected. Using default\n");
		subModel->Mat = Material(); //Default material. 
	}

	//Set reflectivity data. 
	subModel->Mat.Reflection.x = ref.r;
	subModel->Mat.Reflection.y = ref.g;
	subModel->Mat.Reflection.z = ref.b;

	//Is transparant object if opacity < 1.0. Else,
	//is opaque. 
	if(subModel->Mat.Diffuse.w < 1.0f)
		subModel->IsOpaque = false;
	else
		subModel->IsOpaque = true;
}

void StaticModel::LoadAssimpVertexData(ID3D11Device* device, aiMesh* mesh, SubModelData* subModel)
{
	//This is called after we parse the material for this submodel. We therefore
	//know what textures this sub model requires - thus, we know what kind
	//of data we are extracting. 
	//
	//If the model has a displacement map / height map / normal map, we need tangent vectors. If 
	//we do not have a diffuse map to map, we just need pos and normals. Else its pos, norm, texC. 
	//
	//Per vertex structures.
	std::vector<TileForwardSolidColourFX::PosNormalVertex>solidColourVerticies;
	std::vector<TileForwardTextureMapFX::PosNormalTexVertex>texMapVerticies;
	std::vector<TileForwardNormalMapFX::PosNormalTexTangentVertex>tangentVerticies; // Also disp mapping.

	//Enum - Tells us which vector above to write too. 
	enum Type
	{
		TYPE_SOLID_COLOUR = 0, //Pos/Norm
		TYPE_TEX_MAP_ONLY = 1, //Pos/Tex/Norm
		TYPE_TANGENT = 2 //Normal mapping / disp mapping. - Pos/Tex/Normal/Tang 
	};

	Type t;
	if (subModel->HeightMap)
		t = TYPE_TANGENT;
	else if (subModel->NormalMap)
		t = TYPE_TANGENT;
	else if (subModel->TextureMap)
		t = TYPE_TEX_MAP_ONLY;
	else
		t = TYPE_SOLID_COLOUR;

	//Loop through verticies for this mesh and extract in to (one of the...) above vectors.
	for (unsigned i = 0; i < mesh->mNumVertices; i++)
	{
		//Copy position to our system copy vertex vector.
		aiVector3D vP;
		vP = mesh->mVertices[i];
		systemVertexCopyArr.push_back(XMFLOAT3(vP.x, vP.y, vP.z));

		//Copy other data depending (normal etc)
		if (t == TYPE_SOLID_COLOUR)
		{
			//Vertex data
			TileForwardSolidColourFX::PosNormalVertex v;
			
			//Extract only normal data. 
			aiVector3D vN;
			vN = mesh->mNormals[i];

			//Copy Position and normal vector
			v.PosL = XMFLOAT3(vP.x, vP.y, vP.z);
			v.NormalL = XMFLOAT3(vN.x, vN.y, vN.z);

			//Push back on to vector. 
			solidColourVerticies.push_back(v);
		}
		else if (t == TYPE_TEX_MAP_ONLY)
		{
			//Vertex data
			TileForwardTextureMapFX::PosNormalTexVertex v;

			//Extract normal data and tex coord data
			aiVector3D vN = mesh->mNormals[i];
			aiVector3D vT = mesh->mTextureCoords[0][i];

			//Copy position, normal and tex data.
			v.PosL = XMFLOAT3(vP.x, vP.y, vP.z);
			v.NormalL = XMFLOAT3(vN.x, vN.y, vN.z);
			v.Tex = XMFLOAT2(vT.x, vT.y);

			//Push on to vector
			texMapVerticies.push_back(v);
		}
		else if (t == TYPE_TANGENT)
		{
			//Vertex data - Normal mapping / displacement mapping handled here.
			TileForwardNormalMapFX::PosNormalTexTangentVertex v;

			//Extract normal, tex and tangent data.
			aiVector3D vN = mesh->mNormals[i];
			aiVector3D vT = mesh->mTextureCoords[0][i];
			aiVector3D vTng = mesh->mTangents[i];

			//Copy position, normal, tex and tangent data
			v.PosL = XMFLOAT3(vP.x, vP.y, vP.z);
			v.NormalL = XMFLOAT3(vN.x, vN.y, vN.z);
			v.Tex = XMFLOAT2(vT.x, vT.y);
			v.TangentL = XMFLOAT3(vTng.x, vTng.y, vTng.z);

			//Push on to vector
			tangentVerticies.push_back(v);
		}
	}

	//After filling vectors, we need to create vertex buffer. 
	D3D11_BUFFER_DESC bd;
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bd.CPUAccessFlags = 0;
	bd.MiscFlags = 0;
	bd.StructureByteStride = 0;
	bd.Usage = D3D11_USAGE_IMMUTABLE;

	if (t == TYPE_SOLID_COLOUR)
		bd.ByteWidth = sizeof(TileForwardSolidColourFX::PosNormalVertex) * mesh->mNumVertices;
	else if (t == TYPE_TEX_MAP_ONLY)
		bd.ByteWidth = sizeof(TileForwardTextureMapFX::PosNormalTexVertex) * mesh->mNumVertices;
	else if (t == TYPE_TANGENT)
		bd.ByteWidth = sizeof(TileForwardNormalMapFX::PosNormalTexTangentVertex) * mesh->mNumVertices;

	//Init data - above. 
	D3D11_SUBRESOURCE_DATA sd;
	if (t == TYPE_SOLID_COLOUR)
		sd.pSysMem = &solidColourVerticies[0];
	else if (t == TYPE_TEX_MAP_ONLY)
		sd.pSysMem = &texMapVerticies[0];
	else if (t == TYPE_TANGENT)
		sd.pSysMem = &tangentVerticies[0];


	//Create buffer.
	HR(device->CreateBuffer(&bd, &sd, &subModel->vb));

	//Create index buffer. 
	std::vector<unsigned int>ind;
	for (unsigned i = 0; i < mesh->mNumFaces; i++)
	{
		//Get face.
		const aiFace &f = mesh->mFaces[i];

		//assert to ensure 3 indicies per face (triangle)
		assert(f.mNumIndices == 3);

		//Get (3) indicies and push on to vector. 
		ind.push_back(f.mIndices[0]);
		ind.push_back(f.mIndices[1]);
		ind.push_back(f.mIndices[2]);
	}

	bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bd.ByteWidth = sizeof(UINT) * ind.size();
	sd.pSysMem = &ind[0];

	//Create index buffer.
	HR(device->CreateBuffer(&bd, &sd, &subModel->ib));

	//Set submodel topology.
	if (subModel->IsUsingDisplacementMapping)
		subModel->Topology = D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST;
	else
		subModel->Topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;

	//Set draw count for submodel.  
	subModel->DrawCount = ind.size();
	//Increment the total (Every sub model) draw count. 
	this->totalDrawCount += subModel->DrawCount;
}

std::wstring StaticModel::ToWS(std::string s)
{
    int len;
    int slength = (int)s.length() + 1;
    len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0); 
    wchar_t* buf = new wchar_t[len];
    MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
    std::wstring r(buf);
    delete[] buf;
    return r;
}

void StaticModel::LoadTexture(ID3D11Device* device, std::string& file, ID3D11ShaderResourceView** srv)
{
	//We dont support .tga files (that everyone and their dog uses...), so if
	//we are using a .tga format, load the png version instead. Note, this 
	//does mean that you need to convert the file to PNG (Not JPEG as that
	//doesnt support transparancy where TGA does).
	//
	//Copy to intermediate string
	std::string f = file; 
	//Extract last three chars
	std::string fFormat = "   ";
	fFormat[2] = f[f.size()-1];
	fFormat[1] = f[f.size()-2];
	fFormat[0] = f[f.size()-3];

	//If is "tga", change to png
	if (fFormat.compare("tga") == 0)
	{
		//Remove the file format (do so untill we reach a '.')
		char c = ' ';
		while (c != '.')
		{
			f.pop_back();
			c = f[f.size()-1];
		}

		//Replace with png
		f.push_back('p');
		f.push_back('n');
		f.push_back('g');
	}

	//Convert to wstring - .c_str converts to LPCWSTR
	std::wstring fileWS = ToWS(f);


	//Print the file name
	OutputDebugStringW(L"Assimp Loading Texture: ");
	OutputDebugStringW(fileWS.c_str());
	OutputDebugStringW(L"\n");

	//Load the texture map. 
	HR(D3DX11CreateShaderResourceViewFromFile(device, 
		fileWS.c_str(), 0,0, srv, 0));
}

XNA::AxisAlignedBox StaticModel::CalculateWorldSpaceAABB()
{
	XNA::AxisAlignedBox aabbW;

	//Convert from XMFLOAT3 to XMVECTOR
	XMVECTOR r = XMLoadFloat3(&rotation);
	XMVECTOR t = XMLoadFloat3(&position);

	//Transform to world space
	XNA::TransformAxisAlignedBox(&aabbW, &aabbL, scaling.x, r, t);

	return aabbW;
}

XNA::OrientedBox StaticModel::CalculateWorldSpaceOABB()
{
	XNA::OrientedBox oabbW;

	//Decompose the World matrix. 
	XMVECTOR scale;
	XMVECTOR rotQuat;
	XMVECTOR trans;
	XMMatrixDecompose(&scale, &rotQuat, &trans, XMLoadFloat4x4(&worldMatrix));

	//Transform
	XNA::TransformOrientedBox(&oabbW, &oabb, XMVectorGetX(scale), rotQuat, trans);

	//Return 
	return oabbW;
}